#region Copyright (c) 2006-2010 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2010 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Xml;
using System.Linq;
using System.Collections;
using System.ComponentModel;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Common.Util;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Widgetsphere.Generator.Models
{
  public class RelationCollection : BaseModelCollection, IEnumerable<Relation>
  {
    #region Member Variables

    protected List<Relation> innerList = null;
    protected int lastId = 1;

    #endregion

    #region Constructor

    public RelationCollection(IModelObject root)
      : base(root)
    {
			innerList = new List<Relation>();
    }

    #endregion

    #region IXMLable Members

    public override void XmlAppend(XmlNode node)
    {
      try
      {
        XmlDocument oDoc = node.OwnerDocument;

        XmlAttribute keyNode = oDoc.CreateAttribute("key");
        keyNode.Value = this.Key.ToString();
        node.Attributes.Append(keyNode);

        foreach(Relation relation in innerList)
        {
          XmlNode relationNode = oDoc.CreateElement("relation");
          relation.XmlAppend(relationNode);
          node.AppendChild(relationNode);
        }

      }
      catch(Exception ex)
      {
        throw;
      }

    }

    public override void XmlLoad(XmlNode node)
    {
      try
      {
        _key = XmlHelper.GetAttributeValue(node, "key", _key);

        XmlNodeList relationNodes = node.SelectNodes("relation");
        foreach(XmlNode relationNode in relationNodes)
        {
          try
          {
            Relation newRelation = new Relation(this.Root);
						newRelation.XmlLoad(relationNode);
						innerList.Add(newRelation);
          }
          catch { }
        }

        this.Dirty = false;
      }
      catch(Exception ex)
      {
        throw;
      }
    }

    #endregion

    #region Property Implementations

    public ICollection Relations
    {
      get { return innerList; }
    }

		//public ICollection RelationIds
		//{
		//  get { 
		//    return innerList.Keys; }
		//}

    #endregion

    #region Methods

    public Relation GetById(int id)
    {      
      foreach (Relation element in this)
      {
				if (element.Id == id)
					return element;          
      }     
			return null;
    }

		public ReadOnlyCollection<Relation> FindByParentTable(Table table)
		{
			return FindByParentTable(table, false);
		}

		/// <summary>
		/// Find all relationships that have a specific parent table
		/// </summary>
		/// <param name="table">The table from which all relations begin</param>
		/// <param name="includeHierarchy">Determines if the return includes all tables in the inheritence tree</param>		
		/// <returns></returns>
		public ReadOnlyCollection<Relation> FindByParentTable(Table table, bool includeHierarchy)
		{
			List<Table> tableList = null;
			List<Column> columnList = new List<Column>();
			if (includeHierarchy)
			{
				tableList = table.GetTableHierarchy();
				foreach (Table t in tableList)
				{
					foreach (Column column in (from x in t.GetColumnsFullHierarchy(true) select x))
					{
						columnList.Add(column);
					}
				}
			}
			else
			{
				tableList = new List<Table>();
				tableList.Add(table);
				columnList.AddRange(table.GetColumns());
			}

			List<Relation> retval = new List<Relation>();
			foreach (Relation relation in this)
			{
				Table parentTable = (Table)relation.ParentTableRef.Object;
				foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
				{
					foreach (Column column in columnList)
					{
						if (tableList.Contains(parentTable))
						{
							if (!retval.Contains(relation))
								retval.Add(relation);
						}
					}
				}
			}

			return retval.AsReadOnly();
		}

		public ReadOnlyCollection<Relation> FindByChildTable(Table table)
		{
			return FindByChildTable(table, false);
		}

		/// <summary>
		/// Find all relationships that have a specific child table
		/// </summary>
		/// <param name="table">The table from which all relations begin</param>
		/// <param name="includeHierarchy">Determines if the return includes all tables in the inheritence tree</param>		
		/// <returns></returns>
		public ReadOnlyCollection<Relation> FindByChildTable(Table table, bool includeHierarchy)
		{
			List<Table> tableList = null;
			List<Column> columnList = new List<Column>();
			if (includeHierarchy)
			{
				tableList = table.GetTableHierarchy();
				foreach (Table t in tableList)
				{
					foreach (Column column in (from x in t.GetColumnsFullHierarchy(true) select x))
					{
						columnList.Add(column);
					}
				}
			}
			else
			{
				tableList = new List<Table>();
				tableList.Add(table);
				columnList.AddRange(table.GetColumns());
			}

			List<Relation> retval = new List<Relation>();
			foreach (Relation relation in this)
			{
				Table childTable = (Table)relation.ChildTableRef.Object;
				foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
				{
					foreach (Column column in columnList)
					{
						if (tableList.Contains(childTable))						
						{
							if (!retval.Contains(relation))
								retval.Add(relation);
						}
					}
				}
			}

			return retval.AsReadOnly();
		}

		public ReadOnlyCollection<Relation> FindByParentColumn(Column column)
    {
			List<Relation> retval = new List<Relation>();
      foreach(Relation relation in this)
      {        
        foreach(ColumnRelationship columnRelationship in relation.ColumnRelationships)
        {
          if(StringHelper.Match(columnRelationship.ParentColumnRef.Object.Key, column.Key, true))
            retval.Add(relation);
        }
      }
      return retval.AsReadOnly();
    }

		public ReadOnlyCollection<Relation> FindByChildColumn(Column column)
    {
			List<Relation> retval = new List<Relation>();
      foreach(Relation relation in this)
      {
        foreach(ColumnRelationship columnRelationship in relation.ColumnRelationships)
        {
					if (columnRelationship.ChildColumnRef.Object != null)
					{
						if (StringHelper.Match(columnRelationship.ChildColumnRef.Object.Key, column.Key, true))
							retval.Add(relation);
					}
        }
      }
      return retval.AsReadOnly();
    }

		public Relation[] GetFromMatch(Relation relation)
		{
			List<Relation> retval = new List<Relation>();
			try
			{
				foreach (Relation r in this)
				{
					if ((relation.ParentTableRef == null) || (r.ParentTableRef == null) ||
						(relation.ChildTableRef == null) || (r.ChildTableRef == null))
					{
						return null;
					}

					//Verify that parent and child tables match
					if ((((Table)relation.ParentTableRef.Object).Name == ((Table)r.ParentTableRef.Object).Name) && 
						(((Table)relation.ChildTableRef.Object).Name == ((Table)r.ChildTableRef.Object).Name))
					{
						//Same number of column link
						if (relation.ColumnRelationships.Count == r.ColumnRelationships.Count)
						{
							bool match = true;
							for (int ii = 0; ii < relation.ColumnRelationships.Count; ii++)
							{
								if ((relation.ColumnRelationships[ii].ParentColumnRef == null) ||
									(relation.ColumnRelationships[ii].ChildColumnRef == null) ||
									(r.ColumnRelationships[ii].ParentColumnRef == null) ||
									(r.ColumnRelationships[ii].ChildColumnRef == null))
								{
									match = false;
								}
								else
								{
									Column columnChild1 = (Column)relation.ColumnRelationships[ii].ChildColumnRef.Object;
									Table tableChild1 = (Table)columnChild1.ParentTableRef.Object;
									Column columnChild2 = (Column)r.ColumnRelationships[ii].ChildColumnRef.Object;
									Table tableChild2 = (Table)columnChild2.ParentTableRef.Object;

									Column columnParent1 = (Column)relation.ColumnRelationships[ii].ParentColumnRef.Object;
									Table tableParent1 = (Table)columnParent1.ParentTableRef.Object;
									Column columnParent2 = (Column)r.ColumnRelationships[ii].ParentColumnRef.Object;
									Table tableParent2 = (Table)columnParent2.ParentTableRef.Object;

									match |= ((tableChild1.Name == tableChild2.Name) &&
														(columnChild1.Name == columnChild2.Name) &&
														(tableParent1.Name == tableParent2.Name) &&
														(columnParent1.Name == columnParent2.Name));
								}
							}
							if (match) retval.Add(r);
						}
					}
				}
				return retval.ToArray();
			}
			catch (Exception ex)
			{
				throw;
			}

		}

    #endregion

    #region IEnumerable Members

    public override IEnumerator GetEnumerator()
    {
			return innerList.GetEnumerator();
			//ArrayList al = new ArrayList();
			//foreach (int key in innerList.Keys)
			//  al.Add(innerList[key]);
			//return al.GetEnumerator();
    }

    #endregion

		//#region IDictionary Members
		////public bool IsReadOnly
		////{
		////  get { return false; }
		////}

		public Relation this[int index]
		{
			get { return innerList[index]; }
		}

		public Relation GetByName(string name)
		{
			foreach (Relation element in this)
			{
				if (string.Compare(name, element.ConstraintName, true) == 0)
					return element;
			}
			return null;
		}

		public Relation this[string key]
		{
			get
			{
				foreach (Relation element in this)
				{
					if (string.Compare(key, element.Key, true) == 0)
						return element;
				}
				return null;
			}
		}

		////public void Remove(int relationId)
		////{
		////  this.Root.Dirty = true;
		////  innerList.Remove(relationId);
		////}

		public void Remove(Relation element)
		{
			int delCount = 0;
			foreach (Table t in ((ModelRoot)this.Root).Database.Tables)
			{
				List<Reference> delRefList = new List<Reference>();
				foreach (Reference r in t.Relationships)
				{
					if (r.Object == null)
					{
						delCount++;
						delRefList.Add(r);
					}
				}

				//Remove the references
				foreach (Reference r in delRefList)
				{
					t.Relationships.Remove(r);
				}

				if (element != null)
				{
					List<int> delRelationList = new List<int>();
					for (int ii = innerList.Count - 1; ii >= 0; ii--)
					{
						if (innerList[ii].Key == element.Key)
							delRelationList.Add(ii);
					}

					//Remove the references
					foreach (int index in delRelationList)
					{
						innerList.RemoveAt(index);
					}
				}

			}

			innerList.Remove(element);

			//if (this.ContainsId(element.Id))
			//  this.Remove(element.Id);
		}

		//public bool Contains(Relation element)
		//{
		//  foreach (Relation loopEl in this)
		//  {
		//    if (loopEl == element)
		//      return true;
		//  }
		//  return false;
		//}

		////public bool Contains(int relationId)
		////{
		////  return innerList.Contains(relationId);
		////}

		//public bool Contains(string name)
		//{
		//  foreach (Relation element in this)
		//  {
		//    if (string.Compare(element.ConstraintName, name, true) == 0)
		//      return true;
		//  }
		//  return false;
		//}

		public override void Clear()
		{
			for (int ii = this.Count - 1; ii >= 0; ii--)
			{
				this.Remove(this[0]);
			}
		}

		public void Add(Relation value)
		{
			if (this.ContainsId(value.Id))
			{
				lastId = this.GetLastId() + 1;
				value.SetId(lastId);
			}
			innerList.Add(value);
		}

		private bool ContainsId(int id)
		{
			foreach (Relation element in this)
			{
				if (id == element.Id)
					return true;
			}
			return false;
		}

		public bool Contains(Relation item)
		{
			foreach (Relation element in this)
			{
				if (item == element)
					return true;
			}
			return false;
		}

		public bool Contains(string name)
		{
			foreach (Relation element in this)
			{
				if (string.Compare(name, element.ConstraintName, true) == 0)
					return true;
			}
			return false;
		}

		private int GetLastId()
		{
			int retval = int.MinValue;
			foreach (Relation element in this)
			{
				if (retval < element.Id)
					retval = element.Id;
			}
			return retval;
		}

		public override void AddRange(ICollection list)
		{
			foreach (Relation element in list)
			{
				lastId++;
				element.SetId(lastId);
				innerList.Add(element);
			}
		}

		public Relation Add()
		{
			Relation newItem = new Relation(this.Root);
			lastId++;
			newItem.SetId(lastId);
			this.Add(newItem);
			return newItem;
		}

		////public bool IsFixedSize
		////{
		////  get
		////  {
		////    return innerList.IsFixedSize;
		////  }
		////}

		//#endregion

    #region ICollection Members

		public override bool IsSynchronized
		{
			//get { return innerList.IsSynchronized; }
			get { return false; }
		}

    public override int Count
    {
      get { return innerList.Count; }
    }

		public override void CopyTo(Array array, int index)
		{
		  innerList.CopyTo((Relation[])array, index);
		}

		public override object SyncRoot
		{
			//get { return innerList.SyncRoot; }
			get { return null; }
		}

    #endregion

		#region IEnumerable<Relation> Members

		IEnumerator<Relation> IEnumerable<Relation>.GetEnumerator()
		{
			return innerList.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return innerList.GetEnumerator();
		}

		#endregion
	}
}
